#include "ThreadPool.h"
#include "WorkThread.h"
#include "Task.h"
#include <unistd.h>
#include <iostream>

using std::cout;
using std::endl;

ThreadPool::ThreadPool(size_t threadNum, size_t queSize)
: _threadNum(threadNum)
, _queSize(queSize)
/* , _threads(10)   传10个默认值  */
, _taskQue(_queSize)
, _isExit(false)
{
    //预留空间
    _threads.reserve(_threadNum);
}

ThreadPool::~ThreadPool()
{

}

//线程池的启动与停止
//这里和Thread不一样：
//1、Thread：构造函数创建自定义Thread线程对象（实际上还没有真正的线程），
//  start里才create真正的线程，然后等待抢到cpu执行入口函数
//2、pool：start创建很多个工作线程对象（new的时候也调用了Thread的构造函数），
//  然后将这些工作线程分别执行自己的start，才是真正的create，才到func，才是开始运行
//
//总之，创建新线程对象----》create----》func，
//只有start里的create，才是真正创建了线程
//只有执行了func，才是真正开始运行
void ThreadPool::start()
{
    for(size_t idx = 0; idx < _threadNum; ++idx)
    {
        //1、创建工作线程
        unique_ptr<Thread> up(new WorkThread(*this));

        //2、将所有的工作线程放在vector存起来
        //unique_ptr作为容器元素的时候，只能传右值
        _threads.push_back(std::move(up));
        //_threads.push_back(unique_ptr<Thread>(new WorkThread(*this)));
    }

    //3、遍历vector，将所有的工作线程运行起来
    for(auto &th : _threads)
    {
        th->start();
    }
}

void ThreadPool::stop()
{
    //只要任务队列中有任务，就不能让工作线程退出
    while(!_taskQue.empty())
    {
        sleep(1);//睡觉等待队列中的任务执行完
    }
    _isExit = true;//标识线程池的退出

    //唤醒所有在_notEmpty条件变量上的线程
    // _taskQue._notEmpty.notifyAll();
    // pool虽然拥有taskQue，但是notEmpty是私有的，不能访问
    _taskQue.wakeup();

    //1、遍历vector，将所有的工作线程停止运行
    for(auto &th : _threads)
    {
        th->stop();//将所有的工作线程停止运行
    }
}

//添加任务
void ThreadPool::addTask(Task *ptask)
{
    if(ptask)
    {
        _taskQue.push(ptask);
    }
}

//获取任务队列上的第一个任务
Task *ThreadPool::getTask()
{
    return _taskQue.pop();
}

//Q：为何线程池退不出来？
//A：线程池手下的几个工作线程中，有人在wait（阻塞）。
//工作线程在执行doTask函数的时候，会判断_isExit是不是为false，
//如果为false，就会持续getTask拿任务。如果任务队列上有任务就会获取任务然后process。
//同时主线程会等（stop里的循环睡眠）子线程拿完任务之后，才将_isExit设置为true。

//当主线程执行的比较快的时候，有足够的时间将_isExit设置为true，
//那么子线程在执行完process函数之后，就不能进入到while循环中，
//也就是不会再执行getTask拿任务，从而因为没有任务导致阻塞。
//
//但是如果子线程通过getTask拿到任务之后，process的速率比主线程执行的速率要快的话，
//那么可能在主线程执行stop时，还没有来得及将_isExit设置为true，子线程再次进入了doTask的while循环。
//而此时，任务队列中没有任务，子线程wait新的任务从而阻塞，
//即在pop函数的_notEmpty条件变量上睡眠。之后也不会再来新任务_notFull.notify了

//至于有几个人在睡觉，
//可能跑的快一点的在睡觉，（衙门关闭前又跑到公告栏下睡觉，但之后不会再来新的悬赏令了）
//跑的慢的没赶上衙门关闭（_isExit==true），反而不会睡觉，正常结束

//你报nullptr==ptask没有用，因为是在getTask那里阻塞的。
//if(pTask)只是看一看这个任务是不是空白的悬赏令。
//如果是空白的悬赏令or线程池要关闭时不让睡觉，可以打印nullptr，但不会帮助解决阻塞
//
//R：在将_isExit设置为True后，执行一次唤醒wakeup（_notEmpty.notifyAll，衙门要关闭了，公告栏下的镖客们赶紧走吧，你们这次pop空手而归）
//但只是唤醒没用，因为在pop函数里，while(empty())
//此时队列为空，卡在while循环里出不去，下次遍历仍会wait
//所以再添加一个flag，线程池是否还让睡觉
void ThreadPool::doTask()
//线程池交给工作线程做的事件
{
    //只要线程池不退出，那就让工作线程WorkThread一直执行任务
    while(!_isExit)
    {
        //1、先获取任务
        Task *ptask = getTask();
        if(ptask)   //已经拿到了任务，看一看是不是空白任务
        {
            //2、然后执行任务
            ptask->process();//线程池交给工作线程做的任务
            // sleep(3); 相当于4个线程执行4次等一下
        }
        else
        {
            //这里可以优化一下，如果是空白任务，则报nullptr
            //如果是线程池关闭导致返回的是nullptr，则另做说明
            cout << "nullptr == ptask" << endl;
        }
    }
}
